package com.boss.salary.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.boss.common.entity.WfActionDTO;
import com.boss.common.enums.ActionTypeEnum;
import com.boss.common.service.ICommonService;
import com.boss.common.util.DateUtils;
import com.boss.framework.exception.AppRuntimeException;
import com.boss.salary.constant.SystemDictConstant;
import com.boss.salary.dao.IAgencyVerifyDao;
import com.boss.salary.dao.IGenerateSalaryDataDao;
import com.boss.salary.dao.IPayMentLockDao;
import com.boss.salary.entity.AgencyVerify;
import com.boss.salary.entity.MakeSalaryData;
import com.boss.salary.entity.SalaryPaymentRequisitionBack;
import com.boss.salary.service.IGenerateSalaryDataService;
import com.boss.salary.service.IPaymentAsyncService;
import com.boss.salary.service.IRedisService;
import com.boss.salary.workflow.service.ISalaryWorkFlowService;
import com.boss.sys.ca.user.dto.UserDTO;
import com.boss.sys.utils.SysUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@EnableAsync
public class PaymentAsyncServiceImpl implements IPaymentAsyncService{

    private static final Logger logger = LoggerFactory.getLogger(PaymentAsyncServiceImpl.class);
    @Autowired
    private ICommonService commonService;
    @Autowired
    private IRedisService   redisService;
    @Autowired
    private IGenerateSalaryDataDao iGenerateSalaryDataDao;
    @Autowired
    private IAgencyVerifyDao agencyVerifyDao;
    @Autowired
    private ISalaryWorkFlowService iSalaryWorkFlowService;
    @Autowired
    private IPayMentLockDao iPayMentLockDao;
    @Autowired
    private IGenerateSalaryDataService generateSalaryDataService;

    /**
     * 生成工资数据异步
     * @param tokenid
     * @param makeSalaryData 要生成工资数据的单位
     * @return
     */
    @Override
    public Map<String,String> makeSalaryDataAsyncRedis(String tokenid, MakeSalaryData makeSalaryData, UserDTO userDto) {
        String uuid=userDto.getProvince()+"generateSalaryPercent";

        Map<String,String> requestMap=new HashMap<>();
        requestMap.put("message","检验中");
        requestMap.put("guid",uuid);
        requestMap.put("messageKey",userDto.getProvince()+"generateSalaryMessage");

        try {
            checkIsEnoughAsync(tokenid, makeSalaryData, userDto);
            requestMap.put("agencyCount",makeSalaryData.getAgencyCount());
            requestMap.put("personCount",makeSalaryData.getPersonCount());
            requestMap.put("sumRealSalary",makeSalaryData.getSumRealSalary());
        }catch (Exception e){
            redisService.put(uuid,"F-工资统发生成异常"+e.getMessage());
        }

        return  requestMap;
    }
    @Async
    @Override
    public void checkIsEnoughAsync(String tokenid, MakeSalaryData makeSalaryData, UserDTO userDto) {
        String uuid=userDto.getProvince()+"generateSalaryPercent";
        //校验指标是否单位是否挂接指标和收款户
//        String year=agencyVerifyDao.currentYear(makeSalaryData.getAgencyIds());
        List<String> doNotPayCreateAgency=agencyVerifyDao.getDoNotPayCreateAgency(makeSalaryData.getAgencyIds());
        if(CollectionUtils.isEmpty(doNotPayCreateAgency)){
           throw  new AppRuntimeException("无可生成支付单位，请刷新重试！");
        }else{
            makeSalaryData.setAgencyIds(doNotPayCreateAgency);
        }
        StringBuffer request=new StringBuffer();
        logger.info("生成支付：校验指标挂接情况开始");
        String re= generateSalaryDataService.checkPayment(makeSalaryData.getAgencyIds(),userDto);
        List<String> agencyIds=new ArrayList<>();

        if(re!=null&&re.length()>0) {//返回结果不为空且长度大于0则添加到返回结果中
            request.append(re + "\n");
        }
        // 获取已冻结的推送给支付的数据
        logger.info("生成支付：获取指标冻结信息开始");
        List<Map<String, Object>> budgetThawpayList=iPayMentLockDao.selectPayMentLock(makeSalaryData.getAgencyIds());
        //合指标冻结信息
        //指标Id对应agencyId集合
        //冻结合单

        //批量冻结指标
        List<Map<String,Object>> payLockList =new ArrayList<>();
        Map<String,Object> sumIdKeyMap=new HashMap<>();
        Map<String,Object> businessKeyMap=new HashMap<>();
        Map<String,Object> bgtIdKeyAgencyIdMap=new HashMap<>();
        if(!CollectionUtils.isEmpty(budgetThawpayList)) {
            for (Map<String, Object> rapm : budgetThawpayList) {
                bgtIdKeyAgencyIdMap.put(rapm.get("bgt_id").toString(), rapm.get("agency_id").toString());
                //若合单内不存在则直接插入
                if (StringUtils.isEmpty(sumIdKeyMap.get(rapm.get("bgt_id")))) {
                    sumIdKeyMap.put(rapm.get("bgt_id").toString(), new BigDecimal(rapm.get("occup_amt").toString()));
                    businessKeyMap.put(rapm.get("bgt_id").toString(), rapm.get("business_id"));
                } else {//若存在则相加后再进行插入
                    BigDecimal awonser = new BigDecimal(sumIdKeyMap.get(rapm.get("bgt_id")).toString()).add(new BigDecimal(rapm.get("occup_amt").toString()));
                    sumIdKeyMap.put(rapm.get("bgt_id").toString(), awonser);
                    if (!businessKeyMap.get(rapm.get("bgt_id")).equals(rapm.get("business_id"))) {
                        request.append("单位" + rapm.get("agency_id").toString() + "！指标id：" + rapm.get("bgt_id").toString() + "冻结信息存在多条business_id");
                        break;
                    }
                }
            }
            for (String key : sumIdKeyMap.keySet()) {
                Map<String, Object> map = new HashMap<>();

                Map<String, Object> payLockmap = new HashMap<>();
                payLockmap.put("bgt_id", key);
                payLockmap.put("business_id", businessKeyMap.get(key));
                payLockmap.put("payee_acct_no", "");
                payLockmap.put("payee_acct_name", "");
                payLockmap.put("payee_acct_bank_name", "");
                payLockmap.put("occup_amt", sumIdKeyMap.get(key));
                payLockmap.put("agency_id", bgtIdKeyAgencyIdMap.get(key));
                payLockList.add(payLockmap);
            }
        }
        //合指标冻结信息结束
        List<Map<String, Object>>  payList=new ArrayList<>();
        //检查指标是否足够

        List<Map<String, Object>> falsePayList=new ArrayList<>();
        if(!CollectionUtils.isEmpty(makeSalaryData.getAgencyIds())) {
            logger.info("生成支付：校验指标是否充足开始");
            falsePayList = generateSalaryDataService.checkIsEnough(makeSalaryData.getAgencyIds(),payLockList, userDto, payList,false,tokenid);
            agencyIds.addAll(makeSalaryData.getAgencyIds());//另存走异步接口的所有单位信息
            logger.info("生成支付：校验指标是否充足完成："+payList.toString());
        }

        if(!CollectionUtils.isEmpty(falsePayList)) {
            for (Map reMap : falsePayList) {
                request.append(reMap.get("agency_name").toString() + reMap.get("merit_name").toString() + reMap.get("salary_name").toString() + "指标余额不足！\n");
            }
        }
        if(request.toString().length()==0){
            request.append("生成中");
        }
        redisService.put(userDto.getProvince()+"generateSalaryMessage",request.toString());
        if (payList.size() > 0) {//若有可以生成的数据则开始缓存进度

            sentPayMentAsyncForSeate(payList,payLockList, userDto, makeSalaryData, tokenid, uuid,agencyIds);//事务独立的生成支付方法（单个单位走）
        }else{
            redisService.del(uuid);
            redisService.del(userDto.getProvince()+"generateSalary");//无可生成删除key，避免重复操作
        }
    }

    @Async
    @Override
    public void sentPayMentAsyncForSeate(List<Map<String, Object>> payList,List<Map<String, Object>> budgetThawpayList, UserDTO userDto, MakeSalaryData makeSalaryData, String tokenid, String uuid,List<String> totalAgencyIds) {

        StringBuffer requestMessage=new StringBuffer();
        String paymentCreateTime=String.valueOf(DateUtils.getCurrentTimeStamp());//生成支付时间
        int total= totalAgencyIds.size();
        DecimalFormat df=new DecimalFormat("0.00");
        for(int i=0;i<total;i++){
            List<Map<String, Object>> payListOneAgency=new ArrayList<>();
            List<Map<String, Object>> accountListOneAgency=new ArrayList<>();
            for(Map<String, Object> map:payList){
                if(totalAgencyIds.get(i).equals(map.get("agency_id"))) {
                    payListOneAgency.add(map);
                }
            }
            for(Map<String, Object> map:budgetThawpayList){
                if(totalAgencyIds.get(i).equals(map.get("agency_id"))) {
                    accountListOneAgency.add(map);
                }
            }
            logger.info(totalAgencyIds.get(i)+"=======走单个单位生成支付异步方法==============");
            toPayment(payListOneAgency,accountListOneAgency,userDto,makeSalaryData,tokenid,totalAgencyIds.get(i),requestMessage,paymentCreateTime); //调用生成支付方法
            redisService.put(uuid,df.format((float)(i+1)/total));
            logger.info(totalAgencyIds.get(i)+"=======走单个单位生成支付异步方法结束==============");
        }
        if(StringUtils.isEmpty(requestMessage.toString())) {
            redisService.put(uuid, "S");
            redisService.del(userDto.getProvince()+"generateSalary");
            redisService.del(userDto.getProvince()+"generateSalaryMessage");
            redisService.del(userDto.getProvince()+"generateSalarySend");
        }else{
            redisService.put(uuid,requestMessage.toString());
            redisService.del(userDto.getProvince()+"generateSalary");
            redisService.del(userDto.getProvince()+"generateSalaryMessage");
            redisService.del(userDto.getProvince()+"generateSalarySend");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)//@GlobalTransactional(timeoutMills=300000)
    public  void toPayment(List<Map<String, Object>> payListOneAgency,List<Map<String, Object>> accountListOneAgency, UserDTO userDto, MakeSalaryData makeSalaryData, String tokenid,String agencyId,StringBuffer requestMessage, String paymentCreateTime){
        JSONArray jsonArray =new JSONArray();
        Map<String,String> map= commonService.sentPayMent(payListOneAgency, accountListOneAgency,tokenid,userDto.getYear()+"",userDto);//每个单位单独走数据
        JSONObject result = JSONObject.parseObject(map.get("result"));
        String rscode = result.getString("rscode");
        List<String> agencyIds=new ArrayList<>();//反写需要走工作流的单位id
        List<String> payidList=new ArrayList<>();//接成功生成支付得申请id,备用工作流后续流程调用异常时作废申请用
        agencyIds.add(agencyId);
        List<String> businessids= accountListOneAgency.stream().map(accountmap -> (String) accountmap.get("business_id")).collect(Collectors.toList());
        // 调用成功
        if ("200".equals(rscode)) {//将成功的数据单位id保存在集合中，后续处理
            try{
                jsonArray.addAll(result.getJSONArray("data"));
                for (int i = 0; i < jsonArray.size(); i++) {
                    JSONObject jsonObject = jsonArray.getJSONObject(i);
                    // 修改表状态，并添加模拟值
                    payidList.add(jsonObject.getString("id"));
                }
                iPayMentLockDao.batchUpdatePayMentLockByBgtId(businessids,SystemDictConstant.DISABLE);
                savePayBackDatas(jsonArray,userDto,agencyIds,paymentCreateTime);// 保存回单数据
                makeSalaryData.setAgencyIds(agencyIds);
                doSalaryFlow(makeSalaryData, userDto,tokenid);//执行工作流
            }catch (Exception e){
                logger.info("支付生成后，后续方法错误：",e);
//                if(!StringUtils.isEmpty(payidList)) {//报错时若申请支付id不为空则作废支付申请
//                    iCommonService.nullifyPayMent(payidList, tokenid);
//                }
            }
        } else {
            logger.info("存在生成失败信息："+result.getString("result"));
            requestMessage.append("F-"+result.getString("result")+"\n");
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)//@GlobalTransactional(timeoutMills=300000)
    public  void postManToPayment(List<Map<String, Object>> payListList,List<Map<String, Object>> accountList, UserDTO userDto, String tokenid,List<String> agencyIds){
        JSONArray jsonArray =new JSONArray();
        Map<String,String> map= commonService.sentPayMent(payListList, accountList,tokenid,userDto.getYear()+"",userDto);//每个单位单独走数据
        JSONObject result = JSONObject.parseObject(map.get("result"));
        String rscode = result.getString("rscode");
        List<String> payidList=new ArrayList<>();//接成功生成支付得申请id,备用工作流后续流程调用异常时作废申请用

        // 调用成功
        if ("200".equals(rscode)) {//将成功的数据单位id保存在集合中，后续处理
            try{
                jsonArray.addAll(result.getJSONArray("data"));
                for (int i = 0; i < jsonArray.size(); i++) {
                    JSONObject jsonObject = jsonArray.getJSONObject(i);
                    // 修改表状态，并添加模拟值
                    payidList.add(jsonObject.getString("id"));
                }
                savePayBackDatas(jsonArray,userDto,agencyIds,String.valueOf(DateUtils.getCurrentTimeStamp()));// 保存回单数据
            }catch (Exception e){
                logger.info("支付生成后，后续方法错误：",e);
                throw new AppRuntimeException(e);
            }
        } else {
            logger.info("存在生成失败信息："+result.getString("result"));
            throw new AppRuntimeException("F-"+result.getString("result")+"\n");
        }

    }
    /**
     * 保存支付回单数据
     * @param jsonArray
     */

    private void savePayBackDatas(JSONArray jsonArray, UserDTO userDto, List<String> agencyIds,String paymentCreateTime) {
        List<SalaryPaymentRequisitionBack> salaryPaymentRequisitionBacks=new ArrayList<>();
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            // 修改表状态，并添加模拟值
            String agencyId = jsonObject.getString("agency_id");
            SalaryPaymentRequisitionBack salaryPaymentRequisitionBack = new SalaryPaymentRequisitionBack();
            salaryPaymentRequisitionBack.setId(SysUtils.generateGuid());
            salaryPaymentRequisitionBack.setCreateTime(String.valueOf(DateUtils.getCurrentTimeStamp()));
            salaryPaymentRequisitionBack.setUpdateTime(String.valueOf(DateUtils.getCurrentTimeStamp()));
            salaryPaymentRequisitionBack.setAgencyId(agencyId);
            salaryPaymentRequisitionBack.setBizKey(jsonObject.getString("salary_relation_id"));
            salaryPaymentRequisitionBack.setAgencyName(jsonObject.getString("agency_name"));
            salaryPaymentRequisitionBack.setPayId(jsonObject.getString("pay_app_id"));
            salaryPaymentRequisitionBack.setPayAppNo(jsonObject.getString("pay_app_no"));
            salaryPaymentRequisitionBack.setBgtId(jsonObject.getString("bgt_id"));
            salaryPaymentRequisitionBack.setPayAppAmt(jsonObject.getString("pay_app_amt"));
            salaryPaymentRequisitionBack.setFundType(jsonObject.getString("fund_type_name"));
            salaryPaymentRequisitionBack.setPayeeAcctName(jsonObject.getString("payee_acct_name"));
            salaryPaymentRequisitionBack.setPayeeAcctNo(jsonObject.getString("payee_acct_no"));
            salaryPaymentRequisitionBack.setPayeeAcctBankName(jsonObject.getString("payee_acct_bank_name"));
            salaryPaymentRequisitionBack.setPayType(jsonObject.getString("pay_type_name"));
            salaryPaymentRequisitionBack.setProCat(jsonObject.getString("pro_cat_name"));
            salaryPaymentRequisitionBack.setGovBgtEco(jsonObject.getString("gov_bgt_eco_name"));
            salaryPaymentRequisitionBack.setDepBgtEco(jsonObject.getString("dep_bgt_eco_name"));
            salaryPaymentRequisitionBack.setPro(jsonObject.getString("pro_name"));
            salaryPaymentRequisitionBack.setUseName(jsonObject.getString("use_"));
            salaryPaymentRequisitionBack.setExpFunc(jsonObject.getString("exp_func_name"));
            salaryPaymentRequisitionBack.setBusinessId(jsonObject.getString("account_id"));
            salaryPaymentRequisitionBack.setMofDivCode(userDto.getProvince());
            salaryPaymentRequisitionBack.setIsDeleted(SystemDictConstant.IS_FALSE);
            salaryPaymentRequisitionBacks.add(salaryPaymentRequisitionBack);
        }
        iGenerateSalaryDataDao.updatePayDatas(agencyIds, String.valueOf(SystemDictConstant.IS_TRUE),paymentCreateTime);
        iGenerateSalaryDataDao.batchInsertPayBackData(salaryPaymentRequisitionBacks);
    }

    /**
     * 生成支付数据工作流
     * @param makeSalaryData
     * @param userDto
     */
    private void doSalaryFlow(MakeSalaryData makeSalaryData, UserDTO userDto,String tokenid) {

        List<AgencyVerify> agencyVerify1s = agencyVerifyDao.getAgencyVerifyByAgencyIds(makeSalaryData.getAgencyIds());
        // 单位上报录入流程
        WfActionDTO<List<AgencyVerify>> wfActionDTO = new WfActionDTO();
        wfActionDTO.setBizDatas(agencyVerify1s);
        wfActionDTO.setMenuguid(makeSalaryData.getMenuid());
        // 审核
        wfActionDTO.setActionType(ActionTypeEnum.WF_AUDIT.getActionTypeCode());
        wfActionDTO.setBizKeyName("bizKey");
        iSalaryWorkFlowService.doSalaryFlowList(userDto,wfActionDTO,tokenid);
        logger.info("==== 工作流生成工资数据完毕End  ===");
    }
}
